/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 *
 * JavaScript - Base
 * Version: 1.0
 */

// 验证码图片
+function ($) {
    "use strict";

    var CaptchaImage = function (element, options) {
        this.options = options;
        this.$element = $(element);
        this.$captchaId = null;
        this.captchaId = uuid();

        this.init();
    };

    CaptchaImage.prototype.init = function () {
        this.refresh();
        this.$captchaId = $('<input name="captchaId" type="hidden" value="' + this.captchaId + '">').insertAfter(this.$element);
        this.$element.on("click.dexian.captchaImage", $.proxy(this.refresh, this));
    };

    CaptchaImage.prototype.generateSrc = function () {
        return "/captcha?captchaId=" + this.captchaId + "&timestamp=" + new Date().getTime();
    };

    CaptchaImage.prototype.refresh = function () {
        var refreshEvent = $.Event("refresh.dexian.captchaImage");

        this.$element.trigger(refreshEvent);
        if (refreshEvent.isDefaultPrevented()) {
            return;
        }

        this.$element.attr("src", this.generateSrc());
        this.$element.trigger("refreshed.dexian.captchaImage");
    };

    function uuid() {
        var uuidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
        var r;
        var uuid = [];
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
        uuid[14] = "4";

        for (var i = 0; i < 36; i++) {
            if (!uuid[i]) {
                r = 0 | Math.random() * 16;
                uuid[i] = uuidChars[(i == 19) ? (r & 0x3) | 0x8 : r];
            }
        }
        return uuid.join("");
    }

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.captchaImage");
            var options = $.extend({}, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.captchaImage", (data = new CaptchaImage(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.captchaImage;

    $.fn.captchaImage = Plugin;
    $.fn.captchaImage.Constructor = CaptchaImage;

    $.fn.captchaImage.noConflict = function () {
        $.fn.captchaImage = old;
        return this;
    };

    $(window).on("load", function () {
        $("[data-toggle='captchaImage']").each(function () {
            var $this = $(this);

            Plugin.call($this);
        });
    });

}(jQuery);

// 滚动加载
+function ($) {
    "use strict";

    var ScrollLoad = function (element, options) {
        this.options = options;
        this.$element = $(element);
        this.scrollload = null;
        this.page = 0;

        this.init();
    };

    ScrollLoad.DEFAULTS = {
        url: null,
        type: "GET",
        data: null,
        dataType: "json",
        contentTarget: "#scrollLoadContent",
        templateTarget: "#scrollLoadTemplate"
    };

    ScrollLoad.prototype.init = function () {
        var contentTarget = typeof this.options.contentTarget === "function" ? this.options.contentTarget(this.$element) : this.options.contentTarget;
        var $contentTarget = contentTarget instanceof jQuery ? contentTarget : $(contentTarget);

        if (typeof Scrollload === "undefined") {
            throw new Error("ScrollLoad requires scrollload.js");
        }

        this.scrollload = new Scrollload({
            container: this.$element[0],
            content: $contentTarget[0],
            loadMore: $.proxy(this.load, this)
        });
    };

    ScrollLoad.prototype.load = function () {
        var that = this;
        var url = typeof this.options.url === "function" ? this.options.url(this.$element) : this.options.url;
        var type = typeof this.options.type === "function" ? this.options.type(this.$element) : this.options.type;
        var data = typeof this.options.data === "function" ? this.options.data(this.$element) : this.options.data;
        var dataType = typeof this.options.dataType === "function" ? this.options.dataType(this.$element) : this.options.dataType;
        var templateTarget = typeof this.options.templateTarget === "function" ? this.options.templateTarget(this.$element) : this.options.templateTarget;
        var $templateTarget = templateTarget instanceof jQuery ? templateTarget : $(templateTarget);
        var loadEvent = $.Event("load.dexian.scrollLoad");

        if (typeof _ === "undefined") {
            throw new Error("ScrollLoad requires lodash.js");
        }

        this.$element.trigger(loadEvent);
        if (loadEvent.isDefaultPrevented()) {
            return;
        }

        $.ajax({
            url: _.template(url)({
                page: this.page++
            }),
            type: type,
            data: data,
            dataType: dataType
        }).done(function (data) {
            if (data != null && data.length > 0) {
                var template = _.template($templateTarget.html());

                $(that.scrollload.contentDom).append(template({
                    data: data
                }));
                that.scrollload.unLock();
                if ($.fn.lazyload != null) {
                    lazyload();
                }
            } else {
                that.scrollload.noMoreData();
            }
            that.$element.trigger("loaded.dexian.scrollLoad", arguments);
        }).fail(function () {
            that.scrollload.throwException();
        });
    };

    ScrollLoad.prototype.refresh = function () {
        this.page = 0;
        $(this.scrollload.contentDom).empty();
        this.scrollload.refreshData();
    };

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.scrollLoad");
            var options = $.extend({}, ScrollLoad.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.scrollLoad", (data = new ScrollLoad(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.scrollLoad;

    $.fn.scrollLoad = Plugin;
    $.fn.scrollLoad.Constructor = ScrollLoad;

    $.fn.scrollLoad.noConflict = function () {
        $.fn.scrollLoad = old;
        return this;
    };

    $(window).on("load", function () {
        $("[data-spy='scrollLoad']").each(function () {
            var $this = $(this);

            Plugin.call($this);
        });
    });

}(jQuery);

// 删除
+function ($) {
    "use strict";

    var Delete = function (element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.dexian.delete", $.proxy(this.execute, this));
    };

    Delete.DEFAULTS = {
        url: "delete",
        type: "POST",
        data: null,
        dataType: "json",
        confirm: true,
        confirmMessage: "您确定要删除吗？"
    };

    Delete.prototype.confirm = function (callback) {
        var confirmMessage = typeof this.options.confirmMessage === "function" ? this.options.confirmMessage(this.$element) : this.options.confirmMessage;

        if (typeof bootbox === "undefined") {
            throw new Error("Delete requires bootbox.js");
        }

        bootbox.confirm(confirmMessage, $.proxy(callback, this));
    };

    Delete.prototype.ajax = function () {
        var url = typeof this.options.url === "function" ? this.options.url(this.$element) : this.options.url;
        var type = typeof this.options.type === "function" ? this.options.type(this.$element) : this.options.type;
        var data = typeof this.options.data === "function" ? this.options.data(this.$element) : this.options.data;
        var dataType = typeof this.options.dataType === "function" ? this.options.dataType(this.$element) : this.options.dataType;
        return $.ajax({
            url: url,
            type: type,
            data: data,
            dataType: dataType
        });
    };

    Delete.prototype.execute = function () {
        var that = this;
        var confirm = typeof this.options.confirm === "function" ? this.options.confirm(this.$element) : this.options.confirm;
        var beforeEvent = $.Event("before.dexian.delete");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        if (confirm) {
            this.confirm(function (result) {
                if (result == null || !result) {
                    return;
                }

                that.ajax().done(function () {
                    that.$element.trigger("success.dexian.delete", arguments);
                }).fail(function () {
                    that.$element.trigger("error.dexian.delete", arguments);
                }).always(function () {
                    that.$element.trigger("complete.dexian.delete", arguments);
                });
            });
        } else {
            this.ajax().done(function () {
                that.$element.trigger("success.dexian.delete", arguments);
            }).fail(function () {
                that.$element.trigger("error.dexian.delete", arguments);
            }).always(function () {
                that.$element.trigger("complete.dexian.delete", arguments);
            });
        }
    };

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.delete");
            var options = $.extend({}, Delete.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.delete", (data = new Delete(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn["delete"];

    $.fn["delete"] = Plugin;
    $.fn["delete"].Constructor = Delete;

    $.fn["delete"].noConflict = function () {
        $.fn["delete"] = old;
        return this;
    };

    $(document).on("click.dexian.delete.data-api", "[data-action='delete']", function () {
        var $this = $(this);

        if ($this.data("dexian.delete") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 刷新
+function ($) {
    "use strict";

    var Refresh = function (element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.dexian.refresh", $.proxy(this.execute, this));
    };

    Refresh.DEFAULTS = {
        force: true
    };

    Refresh.prototype.execute = function () {
        var force = typeof this.options.force === "function" ? this.options.force(this.$element) : this.options.force;
        var beforeEvent = $.Event("before.dexian.refresh");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        location.reload(force);

        this.$element.trigger("complete.dexian.refresh");
    };

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.refresh");
            var options = $.extend({}, Refresh.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.refresh", (data = new Refresh(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.refresh;

    $.fn.refresh = Plugin;
    $.fn.refresh.Constructor = Refresh;

    $.fn.refresh.noConflict = function () {
        $.fn.refresh = old;
        return this;
    };

    $(document).on("click.dexian.refresh.data-api", "[data-action='refresh']", function () {
        var $this = $(this);

        if ($this.data("dexian.refresh") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 全选
+function ($) {
    "use strict";

    var CheckAll = function (element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("change.dexian.checkAll", $.proxy(this.toggle, this));
    };

    CheckAll.DEFAULTS = {
        target: "input[name='ids']"
    };

    CheckAll.prototype.toggle = function () {
        return this.$element.prop("checked") ? this.check() : this.uncheck();
    };

    CheckAll.prototype.check = function () {
        var target = typeof this.options.target === "function" ? this.options.target(this.$element) : this.options.target;
        var $target = target instanceof jQuery ? target : $(target);
        var checkEvent = $.Event("check.dexian.checkAll");

        this.$element.trigger(checkEvent);
        if (checkEvent.isDefaultPrevented()) {
            return;
        }

        if (!this.$element.prop("checked")) {
            this.$element.prop("checked", true);
        }
        $target.filter(":not(:checked)").prop("checked", true).trigger("change");

        this.$element.trigger("checked.dexian.checkAll");
    };

    CheckAll.prototype.uncheck = function () {
        var target = typeof this.options.target === "function" ? this.options.target(this.$element) : this.options.target;
        var $target = target instanceof jQuery ? target : $(target);
        var uncheckEvent = $.Event("uncheck.dexian.checkAll");

        this.$element.trigger(uncheckEvent);
        if (uncheckEvent.isDefaultPrevented()) {
            return;
        }

        if (this.$element.prop("checked")) {
            this.$element.prop("checked", false);
        }
        $target.filter(":checked").prop("checked", false).trigger("change");

        this.$element.trigger("unchecked.dexian.checkAll");
    };

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.checkAll");
            var options = $.extend({}, CheckAll.DEFAULTS, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.checkAll", (data = new CheckAll(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.checkAll;

    $.fn.checkAll = Plugin;
    $.fn.checkAll.Constructor = CheckAll;

    $.fn.checkAll.noConflict = function () {
        $.fn.checkAll = old;
        return this;
    };

    $(document).on("change.dexian.checkAll.data-api", "[data-toggle='checkAll']", function () {
        var $this = $(this);

        if ($this.data("dexian.checkAll") != null) {
            return;
        }
        Plugin.call($this, "toggle");
    });

}(jQuery);

// 返回
+function ($) {
    "use strict";

    var Back = function (element, options) {
        this.options = options;
        this.$element = $(element);

        this.$element.on("click.dexian.back", $.proxy(this.execute, this));
    };

    Back.prototype.execute = function () {
        var beforeEvent = $.Event("before.dexian.back");

        this.$element.trigger(beforeEvent);
        if (beforeEvent.isDefaultPrevented()) {
            return;
        }

        history.back();

        this.$element.trigger("complete.dexian.back");
    };

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.back");
            var options = $.extend({}, $this.data(), typeof option === "object" && option);

            if (data == null) {
                $this.data("dexian.back", (data = new Back(this, options)));
            }
            if (typeof option === "string") {
                data[option]();
            }
        });
    }

    var old = $.fn.back;

    $.fn.back = Plugin;
    $.fn.back.Constructor = Back;

    $.fn.back.noConflict = function () {
        $.fn.back = old;
        return this;
    };

    $(document).on("click.dexian.back.data-api", "[data-action='back']", function () {
        var $this = $(this);

        if ($this.data("dexian.back") != null) {
            return;
        }
        Plugin.call($this, "execute");
    });

}(jQuery);

// 对话框选择
+(function ($) {
    'use strict';

    var DialogSelect = function (element, options) {
        this.options = options;
        this.$element = $(element);

        this.init();
    };

    DialogSelect.DEFAULTS = {
        valueKey: "id",
        searchable: false,
        modalSize: 'large',
        columns: null,
        rows: null,
        resolveValueUrl: null,
        resolveValueRequestMethod: "GET",
        resolveValueRequestParamsTransformer: _.identity,
        resolveValue: null,
        fetchPageUrl: null,
        fetchPageRequestMethod: "GET",
        fetchPageRequestParamsTransformer: _.identity,
        fetchPage: null
    };

    DialogSelect.MODAL_SIZE = {
        small: "modal-sm",
        default: "",
        large: "modal-lg",
        extraLarge: "modal-xl"
    };

    function template(string, variables) {
        return _.template(string, {interpolate: /{{([\s\S]+?)}}/g})(variables);
    }

    var classNames = {
        DISABLED: 'disabled'
    }

    var Templates = {
        selectedPreview: function (id) {
            var variables = {
                id: id
            };

            return $(template('<div id="{{ id }}" style="max-height: 300px;" class="table-responsive"></div>', variables));
        },
        button: function (id) {
            var variables = {
                id: id
            };

            return $(template('<button id="{{ id }}" type="button" class="btn btn-default">选 择</button>', variables));
        },
        modal: function (id, size, searchable, searchPlaceholder) {
            var searchInput = '<div id="search" class="input-group"> \
                                        <input name="searchValue" class="form-control" type="text" value="" placeholder="{{ searchPlaceholder }}" x-webkit-speech="x-webkit-speech" x-webkit-grammar="builtin:search"> \
                                        <div class="input-group-btn"> \
                                            <button class="btn btn-default"> \
                                                <i class="iconfont icon-search"></i> \
                                            </button> \
                                        </div> \
                                    </div>';

            var modal = '\
                                <div id="{{ id }}" class="modal fade" tabindex="-1"> \
                                    <div class="modal-dialog {{ size }}"> \
                                        <div class="modal-content"> \
                                            <div class="modal-header"> \
                                                <button class="close" type="button" data-dismiss="modal">&times;</button> \
                                                <h5 class="modal-title">选 择</h5> \
                                            </div> \
                                            <div class="modal-body form-horizontal"> \
                                                <div class="panel panel-default"> \
                                                    <div class="panel-heading"> \
                                                        <div class="row"> \
                                                            <div class="col-xs-6 col-sm-9 btn-group"> \
                                                                <button id="refresh" type="button" class="btn btn-default">刷 新</button> \
                                                            </div> \
                                                            <div class="col-xs-6 col-sm-3"> \
                                                                {{ searchInput }} \
                                                            </div> \
                                                        </div> \
                                                    </div> \
                                                    <div class="panel-body"> \
                                                        <div id="table" class="table-responsive"> \
                                                        </div> \
                                                    </div> \
                                                    <div class="panel-footer"> \
                                                        <div style="display: flex; justify-content: space-between; align-items: center;"> \
                                                            <div class="text-black"> \
                                                                已选择<span id=\"selectedCount\">{{ selectedCount }}<\/span>个 \
                                                            </div> \
                                                            <div id="pagination" class="text-right"> \
                                                            </div> \
                                                        </div> \
                                                    </div> \
                                                </div> \
                                            </div> \
                                            <div class="modal-footer"> \
                                                <button class="btn btn-primary" type="submit">确 定</button> \
                                                <button class="btn btn-default" type="button" data-dismiss="modal">取 消</button> \
                                            </div> \
                                        </div> \
                                    </div> \
                                </div> \
                            ';

            var variables = {
                id: id,
                size: DialogSelect.MODAL_SIZE[size],
                searchInput: searchable ? template(searchInput, {searchPlaceholder: searchPlaceholder}) : "",
                selectedCount: 0
            };

            return $(template(modal, variables));
        },
        table: function (columns, rows, data, valueKey, multiple, checkable, removeable) {
            var tableHeaders = Object.keys(columns).map(function (key) {
                var column = columns[key];
                return template('<th scope="col">{{ label }}</th>', {label: typeof column === "object" ? column.label : column});
            });

            var tableRows = data.map(function (item) {
                var tableData = Object.keys(columns).map(function (key) {
                    var row = rows[key];
                    return template('<td>{{ content }}</td>', {content: typeof row === "function" ? row(item) : row})
                });

                var elementData = Object.keys(item).map(function (key) {
                    var value = item[key];

                    return template('data-{{ key}}="{{ value }}"', {
                        key: key, value: value
                    });
                }).join(" ");

                if (checkable) {
                    var checkbox = '<td>\
                                            <div class="checkbox"> \
                                                <input name="ids" type="checkbox" value="{{ value }}" {{ elementData }} {{ checked }}> \
                                                <label></label> \
                                            </div> \
                                        </td>';

                    tableData.unshift(template(checkbox, {
                        value: item[valueKey],
                        elementData: elementData,
                        checked: item["checked"] ? "checked" : ""
                    }))
                }

                if (removeable) {
                    var action = '<td> \
                                            <a href="javascript:;" class="link-primary" data-value="{{ dataValue }}" {{ elementData }}>删除</a> \
                                        </td>';

                    tableData.push(template(action, {
                        dataValue: item[valueKey],
                        elementData: elementData
                    }))
                }

                return template('<tr>{{ tableData }}</tr>', {tableData: tableData.join("\n")});
            });

            if (checkable) {
                var checkAll = multiple ? '<th> <div class="checkbox"><input type="checkbox" data-toggle="checkAll"><label></label></div></th>' : '<th></th>';
                tableHeaders.unshift(template(checkAll))
            }

            if (removeable) {
                tableHeaders.push('<th scope="col">操作</th>')
            }

            var table = '\
                                    <table class="table"> \
                                        <thead> \
                                            <tr>{{ tableHeaders }}</tr> \
                                        </thead> \
                                        <tbody>{{ tableRows }}</tbody> \
                                    </table> \
                                    {{ emptyTips }} \
                                ';

            var variables = {
                tableHeaders: tableHeaders.join("\n"),
                tableRows: tableRows.join("\n"),
                emptyTips: data.length === 0 ? '<p class="no-result">暂无信息</p>' : ''
            };

            return $(template(table, variables));
        },
        pagination: function (hasPrevious, hasNext) {
            var pagination = '<ul class="pagination"> \
                                <li class="{{ previousClass }}"> \
                                    <a id="previous" href="javascript:;">&laquo;</a> \
                                </li> \
                                <li class="{{ nextClass }}"> \
                                    <a id="next" href="javascript:;">&raquo;</a> \
                                </li> \
                            </ul>';

            var variables = {
                previousClass: hasPrevious ? "" : "disabled",
                nextClass: hasNext ? "" : "disabled"
            };

            return $(template(pagination, variables));
        }
    }

    var selectId = 0;

    DialogSelect.prototype = {
        constructor: DialogSelect,
        init: function () {
            var element = this.$element[0];

            this.selectId = selectId++;

            element.classList.add('hidden-element');

            this.validateOptions();

            this.multiple = this.$element.prop('multiple');

            this.$selectedPreview = Templates.selectedPreview('selectedPreview' + selectId);
            this.$button = Templates.button('button' + selectId);

            this.$element.after(this.$selectedPreview);
            this.$element.after(this.$button);

            var that = this;
            this.$button.on('click', function () {
                if (that.isDisabled()) {
                    return;
                }

                if (!that.$modal) {
                    that.$modal = that.createModal();
                }

                that.selectedValue = that.getSelectedValue();
                that.renderTable(0, function () {
                    that.$modal.modal('show');
                });
            });

            this.checkDisabled();
            this.renderPreviewTable(this.getSelectedValue());
        },
        checkDisabled: function () {
            if (this.isDisabled()) {
                this.$button.addClass(classNames.DISABLED).attr('aria-disabled', true);
            } else {
                if (this.$button[0].classList.contains(classNames.DISABLED)) {
                    this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false);
                }
            }
        },
        refresh: function () {
            var config = $.extend({}, this.options, this.$element.data());
            this.options = config;

            this.checkDisabled();
            this.renderPreviewTable(this.getSelectedValue());

            this.$element.trigger('refreshed');
        },
        validateOptions: function () {
            if (this.options.valueKey == null) {
                throw new Error('options "valueKey" must not be null.');
            }

            if (!this.isDisabled() && this.options.fetchPageUrl == null && this.options.fetchPage == null) {
                throw new Error('must config one of ["fetchPageUrl", "fetchPage"]');
            }

            if (!this.isDisabled() && this.options.fetchPageUrl != null && typeof this.options.fetchPageUrl !== "string") {
                throw new TypeError('"fetchPageUrl" must is string type.');
            }

            if (!this.isDisabled() && this.options.fetchPage != null && typeof this.options.fetchPage !== "function") {
                throw new TypeError('"fetchPageUrl" must is function type.');
            }

            if (this.options.resolveValueUrl == null && this.options.resolveValue == null) {
                throw new Error('must config one of ["resolveValueUrl", "resolveValue"]');
            }

            if (this.options.resolveValueUrl != null && typeof this.options.resolveValueUrl !== "string") {
                throw new TypeError('"resolveValueUrl" must is string type.');
            }

            if (this.options.resolveValue != null && typeof this.options.resolveValue !== "function") {
                throw new TypeError('"resolveValue" must is function type.');
            }

            if (Object.keys(this.options.columns).length === 0) {
                throw new Error('options "columns" must not be empty.');
            }

            if (Object.keys(this.options.rows).length === 0) {
                throw new Error('options "rows" must not be empty.');
            }

            var that = this;
            Object.keys(this.options.columns).forEach(function (item) {
                if (that.options.rows[item] == null) {
                    throw new Error('miss ' + item + ' row definition.');
                }

                var type = typeof that.options.rows[item];

                if (type !== "string" && type !== "function") {
                    throw new TypeError('" ' + item + ' row value must is "string" or "function" type');
                }
            });
        },
        createModal: function () {
            var that = this;

            var instance = Templates.modal('dialogSelectModal' + that.selectId, that.options.modalSize, that.options.searchable, that.options.searchPlaceholder || that.$element.attr('title') || "查找...");

            instance.find("#refresh").on('click', _.throttle(function (event) {
                event.preventDefault();

                that.renderTable();
            }, 500));

            if (that.options.searchable) {
                instance.find("#search button").on('click', _.throttle(function (event) {
                    event.preventDefault();

                    that.renderTable();
                }, 500));

                instance.find("#search").keypress(function (event) {
                    if (event.which === 13) {
                        that.renderTable();
                        return false;
                    }
                });
            }

            instance.find("[type='submit']").on('click', function (event) {
                event.preventDefault();

                that.selectOptions(that.selectedValue);
                that.renderPreviewTable(that.selectedValue);
                instance.modal('hide');
            });

            return instance;
        },
        renderTable: function (page, callback) {
            var that = this;
            var searchValue = that.$modal.find("[name='searchValue']").val();

            var fetchPage = that.options.fetchPage || function (data, callback) {
                $.ajax({
                    url: that.options.fetchPageUrl,
                    type: that.options.fetchPageRequestMethod,
                    data: that.options.fetchPageRequestParamsTransformer(data),
                    dataType: "json",
                    cache: false,
                    success: callback
                });
            };

            fetchPage({
                searchValue: searchValue,
                page: page
            }, function (data) {
                var tableData = data.content.map(function (item) {
                    var checked = _.find(that.selectedValue, function (value) {
                        return value == item[that.options.valueKey]
                    });

                    return _.assign({}, item, {checked: checked});
                });

                var table = Templates.table(that.options.columns, that.options.rows, tableData, that.options.valueKey, that.multiple, !that.isDisabled(), false);
                var pagination = Templates.pagination(data.hasPrevious, data.hasNext);

                table.find("input[name='ids']").on("change", function () {
                    var $element = $(this);

                    if ($element.prop("checked")) {
                        if (!that.multiple) {
                            $element.closest("tr").siblings().find("input[name='ids']:checked").prop("checked", false);
                        }

                        var id = _.find(that.selectedValue, function (item) {
                            return item == $element.val()
                        });

                        if (!id) {
                            that.multiple ? that.selectedValue.push($element.val()) : that.selectedValue[0] = $element.val();
                        }

                        if (!that.$element.find(template('option[value="{{ value }}"]', {value: $element.val()})).length) {
                            var $option = $(template('<option value="{{ value }}"></option>', {value: $element.val()}));

                            $option.data($element.data());
                            that.$element.append($option);
                        }
                    } else {
                        var index = _.findIndex(that.selectedValue, function (item) {
                            return item == $element.val()
                        });

                        if (index !== -1) {
                            that.selectedValue.splice(index, 1);
                        }
                    }

                    that.updateSelectedCount();
                });

                pagination.find("#previous").on("click", _.once(function () {
                    if (data.hasPrevious) {
                        that.renderTable(data.number - 1);
                    }
                }));

                pagination.find("#next").on("click", _.once(function () {
                    if (data.hasNext) {
                        that.renderTable(data.number + 1);
                    }
                }));

                that.updateSelectedCount();

                requestAnimationFrame(function () {
                    that.$modal.find("#table").empty().append(table);
                    that.$modal.find("#pagination").empty().append(pagination);
                });

                if (callback) {
                    callback();
                }
            });
        },
        updateSelectedCount: function () {
            var that = this;
            requestAnimationFrame(function () {
                that.$modal.find("#selectedCount").text(that.selectedValue.length);
            });
        },
        getSelectedValue: function () {
            var options = this.$element.children("option");
            var option;
            var value = [];

            for (var i = 0, len = options.length; i < len; i++) {
                option = options[i];

                if (!option.disabled && option.selected) {
                    value.push(option.value === undefined ? option.text : option.value);
                }
            }

            if (!this.multiple) {
                return !value.length ? [] : [value[0]];
            }

            return value;
        },
        renderPreviewTable: function (value) {
            var that = this;
            var resolveValue = that.options.resolveValue || function (params, callback) {
                $.ajax({
                    url: that.options.resolveValueUrl,
                    type: that.options.resolveValueRequestMethod,
                    data: that.options.resolveValueRequestParamsTransformer(params),
                    dataType: "json",
                    cache: false,
                    success: callback
                });
            };

            resolveValue(value, function (data) {
                var table = Templates.table(that.options.columns, that.options.rows, data, that.options.valueKey, that.multiple, false, !that.isDisabled());

                if (!that.isDisabled()) {
                    var selectedValue = _.assign([], value);

                    $(table).find("a").on('click', function (event) {
                        event.preventDefault();

                        var $element = $(this);
                        var value = $element.data("value");
                        var index = _.findIndex(selectedValue, function (item) {
                            return item == value;
                        });

                        if (index !== -1) {
                            selectedValue.splice(index, 1);
                            that.selectOptions(selectedValue);
                            that.renderPreviewTable(selectedValue);
                        }
                    });
                }

                requestAnimationFrame(function () {
                    that.$selectedPreview.empty().append(table);
                });
            });
        },
        selectOptions: function (values) {
            this.$element.val(values).change();
        },
        isDisabled: function () {
            return this.$element[0].disabled;
        },
        changeAll: function (status) {
            if (!this.multiple) {
                return;
            }

            var _status = typeof status === 'undefined' || status;
            var prevSelectedValue = this.getSelectedValue();
            var recentValue = [];

            if (_status) {
                this.$element.find("option").each(function () {
                    recentValue.push($(this).attr("value"));
                });
            }

            var isSameValue = function () {
                return recentValue.every(function (value) {
                    return !!_.find(prevSelectedValue, function (item) {
                        return item == value;
                    })
                });
            }

            if (recentValue.length === prevSelectedValue.length && isSameValue()) {
                return;
            }

            this.selectOptions(recentValue);
            this.renderPreviewTable(recentValue);
        },
        selectAll: function () {
            this.changeAll(true);
        },
        deselectAll: function () {
            this.changeAll(false);
        },
        destroy: function () {
            this.$selectedPreview.remove();
            this.$dialogTrigger.remove();
            this.$modal.modal('dispose');
        }
    }

    function Plugin(option) {
        var args = arguments;

        var _option = option;

        [].shift.apply(args);

        return this.each(function () {
            var $this = $(this);
            var data = $this.data("dexian.dialogSelect");
            var options = $.extend({}, DialogSelect.DEFAULTS, $this.data(), typeof _option === "object" && _option);

            if (data == null) {
                $this.data("dexian.dialogSelect", (data = new DialogSelect(this, options)));
            }

            if (typeof _option === "string" && typeof data[option] === "function") {
                data[_option].apply(data, args)
            }
        });
    }

    var old = $.fn.dialogSelect;

    $.fn.dialogSelect = Plugin;
    $.fn.dialogSelect.Constructor = DialogSelect;

    $.fn.dialogSelect.noConflict = function () {
        $.fn.dialogSelect = old;
        return this;
    };
})(jQuery);

(function ($) {

    var $document = $(document);
    var currentUserLocalStorageKey = "currentUser";
    var redirectUrlPrefix = "/";
    var redirectUrlParameterName = "redirectUrl";
    var loginUrls = {
        admin: "\/admin/login"
    };

    // 货币格式化
    $.currency = function (value, showSign, showUnit) {
        if (value == null) {
            return "";
        }
        var result = (Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2)).toFixed(2);

        if (isNaN(result)) {
            result = 0;
        }

        if (showSign) {
            result = "￥" + result;
        }
        if (showUnit) {
            result += "元";
        }
        return result;
    };

    // 获取当前登录用户
    $.getCurrentUser = function () {
        return JSON.parse(localStorage.getItem(currentUserLocalStorageKey));
    };

    // 设置当前登录用户
    $.setCurrentUser = function (currentUser) {
        localStorage.setItem(currentUserLocalStorageKey, JSON.stringify(currentUser));
    };

    // 移除当前登录用户
    $.removeCurrentUser = function () {
        localStorage.removeItem(currentUserLocalStorageKey);
    };

    // 重定向
    $.redirect = function (redirectUrl) {
        location.href = redirectUrl;
    };

    // 重定向登录页面
    $.redirectLogin = function (options) {
        var settings = $.extend({}, $.redirectLogin.defaults, options);
        var loginUrl = loginUrls[settings.loginType];

        if ($.trim(loginUrl) === "") {
            return;
        }
        if ($.trim(settings.redirectUrl) !== "") {
            loginUrl += (loginUrl.indexOf("?") >= 0 ? "&" : "?") + redirectUrlParameterName + "=" + encodeURIComponent(settings.redirectUrl);
        }
        location.href = loginUrl;
    };

    $.redirectLogin.defaults = {
        loginType: "admin",
        redirectUrl: null
    };

    // AjaxSubmit
    var _ajaxSubmit = $.fn.ajaxSubmit;
    $.fn.ajaxSubmit = function (options, data, dataType, onSuccess) {
        var settings = $.extend({}, $.fn.ajaxSubmit.defaults, options);

        return _ajaxSubmit.call(this, settings, data, dataType, onSuccess);
    };

    $.fn.ajaxSubmit.defaults = {
        successMessage: true,
        successRedirectUrl: function (redirectUrlParameterName, data, textStatus, xhr, $form) {
            return new URI().search(true)[redirectUrlParameterName];
        },
        beforeSubmit: function (arr, $form, options) {
            var beforeEvent = $.Event("before.dexian.ajaxSubmit", arguments);

            $form.trigger(beforeEvent);
            if (beforeEvent.isDefaultPrevented()) {
                return false;
            }
            $form.find(":submit").addClass("btn-before-submit").prop("disabled", true);
        },
        success: function (data, textStatus, xhr, $form) {
            var successMessage = $.isFunction(this.successMessage) ? this.successMessage.call(this, redirectUrlParameterName, data, textStatus, xhr, $form) : this.successMessage;
            var successRedirectUrl = $.isFunction(this.successRedirectUrl) ? this.successRedirectUrl.call(this, redirectUrlParameterName, data, textStatus, xhr, $form) : this.successRedirectUrl;

            $form.trigger("success.dexian.ajaxSubmit", arguments);
            if ($.bootstrapGrowl != null && successMessage) {
                var message = typeof successMessage === "string" ? successMessage : data.message;

                if ($.trim(message) !== "") {
                    $.bootstrapGrowl(message, {
                        type: "success"
                    });

                    if ($.trim(successRedirectUrl) !== "") {
                        setTimeout(function () {
                            $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
                            $.redirect(successRedirectUrl);
                        }, 3000);
                        return;
                    }
                }
            }
            if ($.trim(successRedirectUrl) !== "") {
                $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
                $.redirect(successRedirectUrl);
            }
        },
        error: function (xhr, textStatus, error, $form) {
            $form.find(":submit").prop("disabled", false).removeClass("btn-before-submit");
            $form.trigger("error.dexian.ajaxSubmit", arguments);
        },
        complete: function (xhr, textStatus, $form) {
            $form.trigger("complete.dexian.ajaxSubmit", arguments);
        }
    };

    // AJAX全局设置
    $.ajaxSetup({
        traditional: true,
        statusCode: {
            400: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请求参数错误";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            401: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请登录后继续操作";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "warning"
                    });
                }
                setTimeout(function () {
                    $.redirectLogin({
                        redirectUrl: new URI().resource()
                    });
                }, 3000);
            },
            403: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "禁止访问";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            404: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "资源未找到";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            405: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "不支持的请求方式";

                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
            422: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "请求无法处理";
                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "warning"
                    });
                }
            },
            500: function (xhr, textStatus, errorThrown) {
                var data = JSON.parse(xhr.responseText);
                var message = data.message != null ? data.message : "系统出错啦";
                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl(message, {
                        type: "danger"
                    });
                }
            },
        }
    });

    // Cookie
    if ($.cookie != null) {
        $.extend($.cookie.defaults, {
            path: "\/"
        });
    }

    // 提示框
    if ($.bootstrapGrowl != null) {
        $.extend($.bootstrapGrowl.default_options, {
            width: "auto",
            offset: {
                from: "top",
                amount: 120
            },
            align: "center",
            allow_dismiss: false
        });
    }

    // 弹出框
    if (typeof (bootbox) !== "undefined") {
        bootbox.setDefaults({
            locale: "zh_CN",
            title: "提示信息"
        });

        bootbox.addLocale("zh_CN", {
            OK: "确 定",
            CANCEL: "取 消",
            CONFIRM: "确 定"
        });
    }

    // 下拉菜单
    if ($.fn.selectpicker != null) {
        $.fn.selectpicker.defaults = $.extend($.fn.selectpicker.defaults, {
            noneSelectedText: "没有选中任何内容",
            noneResultsText: "没有找到匹配内容",
            countSelectedText: "选中{1}中的{0}项",
            maxOptionsText: "超出限制(最多选择{n}项)",
            selectAllText: "全选",
            deselectAllText: "不选",
            multipleSeparator: "、"
        });
    }

    // 下拉菜单搜索
    if ($.fn.ajaxSelectPicker != null) {
        $.extend($.fn.ajaxSelectPicker.defaults, {
            langCode: "zh_CN"
        });

        $.fn.ajaxSelectPicker.locale["zh_CN"] = {
            currentlySelected: "当前选择",
            emptyTitle: "选择或输入",
            errorText: "没有找到匹配内容",
            searchPlaceholder: "查找...",
            statusInitialized: "输入查找内容",
            statusNoResults: "没有找到任何结果",
            statusSearching: "正在查找..."
        }
    }

    // 选择框
    if ($.fn.checkboxX != null) {
        $.extend($.fn.checkboxX.defaults, {
            size: "xs",
            threeState: false,
            valueChecked: "true",
            valueUnchecked: "false"
        });
    }

    // 日期选择
    if ($.fn.datetimepicker != null) {
        $.extend($.fn.datetimepicker.defaults, {
            locale: moment.locale("zh_CN"),
            format: $(this).data("date-format") != null ? $(this).data("date-format") : "YYYY-MM-DD"
        });
    }

    // 文件上传
    if ($.fn.fileinput != null) {
        $.extend($.fn.fileinput.defaults, {
            language: "zh_CN"
        });

        $.fn.fileinputLocales["zh_CN"] = {
            fileSingle: "文件",
            filePlural: "个文件",
            browseLabel: "选择...",
            removeLabel: "移除",
            removeTitle: "清除选中文件",
            cancelLabel: "取消",
            cancelTitle: "取消正在上传的文件",
            uploadLabel: "上传",
            uploadTitle: "上传选中文件",
            msgNo: "没有",
            msgNoFilesSelected: "没有选中文件",
            msgCancelled: "取消",
            msgPlaceholder: "选择{files}...",
            msgZoomModalHeading: "预览",
            msgFileRequired: "必须选择一个文件",
            msgSizeTooSmall: "文件 {name} 不允许小于{minSize}KB",
            msgSizeTooLarge: "文件 {name} 不允许大于{maxSize}KB",
            msgFilesTooLess: "上传文件数不允许小于{n}",
            msgFilesTooMany: "上传文件数不允许大于{m}",
            msgFileNotFound: "文件不存在 {name}",
            msgFileSecured: "文件无法读取 {name}",
            msgFileNotReadable: "文件无法读取 {name}",
            msgFilePreviewAborted: "取消 {name} 预览",
            msgFilePreviewError: "读取 {name} 错误",
            msgInvalidFileName: "文件名错误 {name}",
            msgInvalidFileType: "文件类型错误 {name}",
            msgInvalidFileExtension: "文件类型错误 {name}",
            msgFileTypes: {
                image: "图片",
                html: "HTML",
                text: "text",
                video: "video",
                audio: "audio",
                flash: "flash",
                pdf: "PDF",
                object: "object"
            },
            msgUploadAborted: "文件上传被中止",
            msgUploadThreshold: "处理中...",
            msgUploadBegin: "初始化...",
            msgUploadEnd: "上传成功",
            msgUploadEmpty: "暂无可上传文件",
            msgUploadError: "上传文件错误",
            msgValidationError: "验证错误",
            msgLoading: "加载第 {index} 文件 共 {files}...",
            msgProgress: "加载第 {index} 文件 共 {files} - {name} - {percent}% 完成",
            msgSelected: "{n} {files} 选中",
            msgFoldersNotAllowed: "只支持拖拽文件！跳过 {n} 拖拽的文件夹",
            msgImageWidthSmall: "文件 {name} 宽度不允许小于{size}像素",
            msgImageHeightSmall: "文件 {name} 高度不允许小于{size}像素",
            msgImageWidthLarge: "文件 {name} 宽度不允许大于{size}像素",
            msgImageHeightLarge: "文件 {name} 高度不允许大于{size}像素",
            msgImageResizeError: "无法调整图片大小",
            msgImageResizeException: "调整图片大小错误 {errors}",
            msgAjaxError: "{operation} 失败",
            msgAjaxProgressError: "{operation} 失败",
            ajaxOperations: {
                deleteThumb: "删除",
                uploadThumb: "上传",
                uploadBatch: "上传",
                uploadExtra: "上传"
            },
            dropZoneTitle: "拖拽文件到这里",
            dropZoneClickTitle: "(或点击{files}按钮选择文件)",
            fileActionSettings: {
                removeTitle: "删除",
                uploadTitle: "上传",
                uploadRetryTitle: "重试",
                downloadTitle: "下载",
                zoomTitle: "查看",
                dragTitle: "移动 / 重置",
                indicatorNewTitle: "没有上传",
                indicatorSuccessTitle: "上传",
                indicatorErrorTitle: "上传错误",
                indicatorLoadingTitle: "上传..."
            },
            previewZoomButtonTitles: {
                prev: "上一个",
                next: "下一个",
                toggleheader: "缩放",
                fullscreen: "全屏",
                borderless: "无边界模式",
                close: "关闭"
            }
        };
    }

    // 文本编辑器
    if ($.summernote != null) {
        $.extend($.summernote.options, {
            lang: "zh_CN",
            minHeight: 300,
            dialogsInBody: true,
            dialogsFade: true,
            callbacks: {
                onImageUpload: function (files) {
                    var $element = $(this);
                    var ajaxs = $.map(files, function (file) {
                        var formData = new FormData();

                        formData.append("fileType", "IMAGE");
                        formData.append("file", file);

                        return $.ajax({
                            url: "/admin/upload",
                            type: "POST",
                            data: formData,
                            dataType: "json",
                            contentType: false,
                            cache: false,
                            processData: false
                        });
                    });
                    var deferred = $.Deferred().resolve();

                    $.when.apply($, ajaxs).done(function () {
                        $.each(arguments, function (index, value) {
                            var url = $.isArray(value) ? value[0].result : value.result;

                            deferred = deferred.then(function () {
                                return $element.summernote("insertImage", url)
                            });
                        });
                    });
                }
            }
        });

        $.extend($.summernote.lang, {
            "zh_CN": {
                font: {
                    bold: "粗体",
                    italic: "斜体",
                    underline: "下划线",
                    clear: "清除格式",
                    height: "行高",
                    name: "字体",
                    strikethrough: "删除线",
                    subscript: "下标",
                    superscript: "上标",
                    size: "字号"
                },
                image: {
                    image: "图片",
                    insert: "插入图片",
                    resizeFull: "缩放至 100%",
                    resizeHalf: "缩放至 50%",
                    resizeQuarter: "缩放至 25%",
                    floatLeft: "靠左浮动",
                    floatRight: "靠右浮动",
                    floatNone: "取消浮动",
                    shapeRounded: "形状: 圆角",
                    shapeCircle: "形状: 圆",
                    shapeThumbnail: "形状: 缩略图",
                    shapeNone: "形状: 无",
                    dragImageHere: "将图片拖拽至此处",
                    dropImage: "图片拖拽",
                    selectFromFiles: "从本地上传",
                    maximumFileSize: "文件大小最大值",
                    maximumFileSizeError: "文件大小超出最大值",
                    url: "图片地址",
                    remove: "移除图片"
                },
                video: {
                    video: "视频",
                    videoLink: "视频链接",
                    insert: "插入视频",
                    url: "视频地址",
                    providers: "(优酷, Instagram, DailyMotion, Youtube等)"
                },
                link: {
                    link: "链接",
                    insert: "插入链接",
                    unlink: "去除链接",
                    edit: "编辑链接",
                    textToDisplay: "显示文本",
                    url: "链接地址",
                    openInNewWindow: "在新窗口打开"
                },
                table: {
                    table: "表格"
                },
                hr: {
                    insert: "水平线"
                },
                style: {
                    style: "样式",
                    p: "普通",
                    blockquote: "引用",
                    pre: "代码",
                    h1: "标题 1",
                    h2: "标题 2",
                    h3: "标题 3",
                    h4: "标题 4",
                    h5: "标题 5",
                    h6: "标题 6"
                },
                lists: {
                    unordered: "无序列表",
                    ordered: "有序列表"
                },
                options: {
                    help: "帮助",
                    fullscreen: "全屏",
                    codeview: "源代码"
                },
                paragraph: {
                    paragraph: "段落",
                    outdent: "减少缩进",
                    indent: "增加缩进",
                    left: "左对齐",
                    center: "居中对齐",
                    right: "右对齐",
                    justify: "两端对齐"
                },
                color: {
                    recent: "最近使用",
                    more: "更多",
                    background: "背景",
                    foreground: "前景",
                    transparent: "透明",
                    setTransparent: "透明",
                    reset: "重置",
                    resetToDefault: "默认"
                },
                shortcut: {
                    shortcuts: "快捷键",
                    close: "关闭",
                    textFormatting: "文本格式",
                    action: "动作",
                    paragraphFormatting: "段落格式",
                    documentStyle: "文档样式",
                    extraKeys: "额外按键"
                },
                help: {
                    insertParagraph: "插入段落",
                    undo: "撤销",
                    redo: "重做",
                    tab: "增加缩进",
                    untab: "减少缩进",
                    bold: "粗体",
                    italic: "斜体",
                    underline: "下划线",
                    strikethrough: "删除线",
                    removeFormat: "清除格式",
                    justifyLeft: "左对齐",
                    justifyCenter: "居中对齐",
                    justifyRight: "右对齐",
                    justifyFull: "两端对齐",
                    insertUnorderedList: "无序列表",
                    insertOrderedList: "有序列表",
                    outdent: "减少缩进",
                    indent: "增加缩进",
                    formatPara: "设置选中内容样式为 普通",
                    formatH1: "设置选中内容样式为 标题1",
                    formatH2: "设置选中内容样式为 标题2",
                    formatH3: "设置选中内容样式为 标题3",
                    formatH4: "设置选中内容样式为 标题4",
                    formatH5: "设置选中内容样式为 标题5",
                    formatH6: "设置选中内容样式为 标题6",
                    insertHorizontalRule: "插入水平线",
                    "linkDialog.show": "显示链接对话框"
                },
                history: {
                    undo: "撤销",
                    redo: "重做"
                },
                specialChar: {
                    specialChar: "特殊字符",
                    select: "选择特殊字符"
                }
            }
        });
    }

    // 滚动固定
    if ($.fn.scrollToFixed != null) {
        $.extend($.ScrollToFixed.defaultOptions, {
            zIndex: 100,
            spacerClass: "scroll-to-fixed-spacer"
        });
    }

    // 滚动加载
    if (typeof (Scrollload) !== "undefined") {
        Scrollload.setGlobalOptions({
            loadingHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            noMoreDataHtml: '<p class="scrollload-no-more-data">没有更多数据</p>',
            exceptionHtml: '<p class="scrollload-exception">加载数据异常</p>',
            notEnoughRefreshPortHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            overRefreshPortHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>',
            refreshingHtml: '<div class="scrollload-loader"><span></span><span></span><span></span><span></span><span></span></div>'
        });
    }

    // 表单验证
    if ($.validator != null) {
        var _greaterThan = $.validator.methods.greaterThan;
        var _greaterThanEqual = $.validator.methods.greaterThanEqual;
        var _lessThan = $.validator.methods.lessThan;
        var _lessThanEqual = $.validator.methods.lessThanEqual;

        $.validator.methods.greaterThan = function (value, element, param) {
            return this.optional(element) || _greaterThan.call(this, Number(value), element, param);
        }
        $.validator.methods.greaterThanEqual = function (value, element, param) {
            return this.optional(element) || _greaterThanEqual.call(this, Number(value), element, param);
        }
        $.validator.methods.lessThan = function (value, element, param) {
            return this.optional(element) || _lessThan.call(this, Number(value), element, param);
        }
        $.validator.methods.lessThanEqual = function (value, element, param) {
            return this.optional(element) || _lessThanEqual.call(this, Number(value), element, param);
        }
        $.validator.addMethod("positive", function (value, element) {
            return this.optional(element) || value > 0;
        }, "A positive number please");

        $.validator.addMethod("negative", function (value, element) {
            return this.optional(element) || value < 0;
        }, "A negative number please");

        $.validator.addMethod("decimal", function (value, element, param) {
            return this.optional(element) || new RegExp("^-?\\d{1," + (param.integer != null ? param.integer : "") + "}" + (param.fraction != null ? (param.fraction > 0 ? "(\\.\\d{1," + param.fraction + "})?$" : "$") : "(\\.\\d+)?$")).test(value);
        }, "Numeric value out of bounds");

        $.validator.addMethod("username", function (value, element) {
            return this.optional(element) || /^[0-9a-zA-Z_\u4e00-\u9fa5]+$/.test(value);
        }, "Only Chinese characters, letters, numbers and underscore can be used");

        $.validator.addMethod("zipCode", function (value, element) {
            return this.optional(element) || /^\d{6}$/.test(value);
        }, "The zip code is invalid");

        $.validator.addMethod("mobile", function (value, element) {
            return this.optional(element) || /^1[3|4|5|6|7|8|9]\d{9}$/.test(value);
        }, "Please enter a valid mobile number");

        $.validator.addMethod("phone", function (value, element) {
            return this.optional(element) || /^\d{3,4}-?\d{7,9}$/.test(value);
        }, "Please enter a valid phone number");

        $.validator.addMethod("notAllNumber", function (value, element) {
            return this.optional(element) || /^.*[^\d].*$/.test(value);
        }, "All numbers are not allowed");

        $.validator.addMethod("equals", function (value, element, param) {
            if (typeof param === "object") {
                return this.optional(element) || (param.ignoreCase ? param.value.toLowerCase() === value.toLowerCase() : param.value === value);
            } else {
                return this.optional(element) || param === value;
            }
        }, "Please enter a value equals {0}.");

        $.validator.addMethod("notEquals", function (value, element, param) {
            return this.optional(element) || !$.validator.methods.equals.call(this, value, element, param);
        }, "Please enter a value not equals {0}.");

        $.validator.setDefaults({
            ignore: ".ignore, .note-editable",
            ignoreTitle: true,
            errorElement: "span",
            errorClass: "help-block",
            normalizer: function (value) {
                return $.trim(value);
            },
            highlight: function (element, errorClass, validClass) {
                $(element).closest("td, .form-group").addClass("has-warning").removeClass("was-validated-success");
            },
            unhighlight: function (element, errorClass, validClass) {
                $(element).closest("td, .form-group").removeClass("has-warning").addClass("was-validated-success");
            },
            errorPlacement: function ($error, $element) {
                var $formGroup = $element.closest("td, [class^='col-'], .form-group");
                if ($formGroup.length > 0) {
                    $error.appendTo($formGroup);
                } else {
                    $error.insertAfter($element);
                }
            },
            invalidHandler: function (event, validator) {
                if ($.bootstrapGrowl != null) {
                    $.bootstrapGrowl("输入有误，请检查后重新提交", {
                        type: "warning"
                    });
                }
            },
            submitHandler: function (form) {
                var $form = $(form);

                if ($.fn.ajaxForm != null && $form.hasClass("ajax-form")) {
                    $form.ajaxSubmit();
                } else {
                    form.submit();
                }
            }
        });

        $.extend($.validator.messages, {
            required: "必填",
            remote: "输入错误",
            email: "E-mail格式错误",
            url: "URL格式错误",
            date: "日期格式错误",
            dateISO: "日期格式错误",
            number: "只允许输入数字",
            digits: "只允许输入零或正整数",
            equalTo: "两次输入不一致",
            maxlength: $.validator.format("长度不允许大于{0}"),
            minlength: $.validator.format("长度不允许小于{0}"),
            rangelength: $.validator.format("长度必须在{0}-{1}之间"),
            range: $.validator.format("必须在{0}-{1}之间"),
            max: $.validator.format("不允许大于{0}"),
            min: $.validator.format("不允许小于{0}"),
            step: $.validator.format("必须在{0}之间"),
            maxWords: $.validator.format("最大允许输入{0}个单词"),
            minWords: $.validator.format("最小允许输入{0}个单词"),
            rangeWords: $.validator.format("只允许输入{0}-{1}个单词"),
            accept: $.validator.format("文件类型错误"),
            alphanumeric: "只允许输入字母、数字和下划线",
            bankaccountNL: "银行帐号格式错误",
            bankorgiroaccountNL: "银行帐号格式错误",
            bic: "BIC格式错误",
            cifES: "CIF格式错误",
            cpfBR: "CPF格式错误",
            creditcard: "信用卡格式错误",
            creditcardtypes: "信用卡格式错误",
            currency: "货币格式错误",
            dateFA: "日期格式错误",
            dateITA: "日期格式错误",
            dateNL: "日期格式错误",
            extension: $.validator.format("文件扩展名错误"),
            giroaccountNL: "汇款帐号格式错误",
            greaterThan: "输入值要求大于最小值",
            greaterThanEqual: "输入值要求大于等于最小值",
            iban: "IBAN格式错误",
            integer: "只允许输入整数",
            ipv4: "IPv4格式错误",
            ipv6: "IPv6格式错误",
            lessThan: "输入值要求小于最大值",
            lessThanEqual: "输入值要求小于等于最大值",
            lettersonly: "只允许输入字母",
            letterswithbasicpunc: "只允许输入字母和标点符号",
            maxfiles: "请选择不超过{0}个文件",
            maxsize: "单个文件大小不能超过{0}字节",
            maxsizetotal: "总文件大小不能超过{0}字节",
            mobileNL: "手机格式错误",
            mobileUK: "手机格式错误",
            netmask: "子网掩码格式错误",
            nieES: "NIE格式错误",
            nifES: "NIF格式错误",
            nipPL: "NIP格式错误",
            notEqualTo: "两次输入不允许相同",
            nowhitespace: "不允许输入空白字符",
            pattern: "格式错误",
            phoneNL: "电话号码格式错误",
            phonesUK: "电话号码格式错误",
            phoneUK: "电话号码格式错误",
            phoneUS: "电话号码格式错误",
            postalcodeBR: "邮编格式错误",
            postalCodeCA: "邮编格式错误",
            postalcodeIT: "邮编格式错误",
            postalcodeNL: "邮编格式错误",
            postcodeUK: "邮编格式错误",
            require_from_group: $.validator.format("必须填写{0}内容"),
            skip_or_fill_minimum: $.validator.format("必须填写{0}内容"),
            stateUS: "格式错误",
            strippedminlength: $.validator.format("请输入至少{0}个字符"),
            time: "时间格式错误",
            time12h: "时间格式错误",
            url2: "URL格式错误",
            vinUS: "VIN格式错误",
            zipCode: "邮编格式错误",
            zipcodeUS: "邮编格式错误",
            ziprange: "邮编范围应在902xx-xxxx和905xx-xxxx之间",
            positive: "只允许输入正数",
            negative: "只允许输入负数",
            decimal: "数值超出了允许范围",
            username: "只允许包含中文、英文、数字、下划线",
            mobile: "手机格式错误",
            phone: "电话号码格式错误",
            notAllNumber: "不允许全部为数字",
            equals: "请输入与{0}相等的值",
            notEquals: "请输入与{0}不相等的值"
        });
    }

})(jQuery);

$().ready(function () {

    var $document = $(document);
    var $body = $("body");
    var $form = $("form");
    var $redirectUrl = $("a[data-redirect-url]");
    var redirectUrlParameterName = "redirectUrl";
    var userAgent = navigator.userAgent.toLowerCase();
    var userAgentMatch = /(chrome)[ \/]([\w.]+)/.exec(userAgent) || /(webkit)[ \/]([\w.]+)/.exec(userAgent) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(userAgent) || /(msie) ([\w.]+)/.exec(userAgent) || userAgent.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(userAgent) || [];
    var $countdown = $("[data-target='countdown']");

    // 倒计时
    if ($.fn.animateNumber != null) {
        $countdown.prop("number", 3).animateNumber({
            number: 0,
            numberStep: function (now, tween) {
                var $target = $(tween.elem);
                var rounded_now = Math.round(now);

                if (now === tween.end) {
                    window.location.href = "/"
                } else {
                    $target.text(rounded_now);
                }
            }
        }, 3000, "linear");
    }

    // 浏览器不支持错误
    if (userAgentMatch[1] != null) {
        var browser = userAgentMatch[1];
        var version = userAgentMatch[2];
        var majorVersion = /^\d+/i.exec(version);

        if (browser === "msie" && parseFloat(majorVersion) < 10) {
            $("html, body").css({
                width: "100%",
                height: "100%",
                overflow: "hidden"
            }).append('<iframe class="unsupported-browser-iframe" src="/common/error/unsupported_browser" frameborder="0" scrolling="no" allowtransparency="true"></iframe>');
        }
    }

    // 用户注册/登录
    $document.on("registered.dexian.user loggedIn.dexian.user", function (event, currentUser) {
        $.setCurrentUser(currentUser);
    });

    // 用户注销
    $document.on("loggedOut.dexian.user", function (event, currentUser) {
        $.removeCurrentUser();
    });

    // CSRF令牌
    $document.ajaxSend(function (event, xhr, settings) {
        var type = settings.type != null ? settings.type : "GET";

        if (!/^(GET|HEAD|TRACE|OPTIONS)$/i.test(type) && !settings.crossDomain) {
            var csrfToken = $.cookie("XSRF-TOKEN");
            if (csrfToken != null) {
                xhr.setRequestHeader("X-XSRF-TOKEN", csrfToken);
            }
        }
    });

    // CSRF令牌
    $form.submit(function () {
        var $element = $(this);
        var $csrfToken = $element.find("input[name='_csrf']");
        var method = $element.attr("method") != null ? $element.attr("method") : "GET";

        if (!/^(GET|HEAD|TRACE|OPTIONS)$/i.test(method) && $csrfToken.length <= 0) {
            var csrfToken = $.cookie("XSRF-TOKEN");
            if (csrfToken != null) {
                $element.append('<input name="_csrf" type="hidden" value="' + csrfToken + '">');
            }
        }
    });

    // AJAX表单
    if ($.fn.ajaxForm != null) {
        $("form.ajax-form").submit(function () {
            var $element = $(this);
            var validator = $element.data("validator");

            if (validator != null) {
                return;
            }

            $element.ajaxSubmit();
            return false;
        });
    }

    // 重定向URL
    $redirectUrl.click(function () {
        var $element = $(this);
        var href = $element.attr("href");
        var redirectUrl = $element.data("redirect-url");

        if ($.trim(redirectUrl) === "") {
            redirectUrl = new URI().resource();
        }

        location.href = URI(href).setSearch(redirectUrlParameterName, redirectUrl).toString();
        return false;
    });

    // 下拉菜单
    if ($.validator != null) {
        $("select.selectpicker").change(function () {
            var $element = $(this);
            var validator = $element.closest("form").validate();

            if (validator != null) {
                validator.element($element);
            }
        });
    }

    // 日期选择
    if ($.fn.datetimepicker != null) {
        $("[data-provide='datetimepicker']").datetimepicker();

        $("[data-provide='datetimerangepicker']").each(function () {
            var $element = $(this);
            var $startDateTimePicker = $element.find("input:text:eq(0)");
            var $endDateTimePicker = $element.find("input:text:eq(1)");

            $startDateTimePicker.datetimepicker({
                format: $element.data("date-format")
            }).on("dp.change", function (event) {
                $endDateTimePicker.data("DateTimePicker").minDate(event.date);
            });

            $endDateTimePicker.datetimepicker({
                format: $element.data("date-format"),
                useCurrent: false
            }).on("dp.change", function (event) {
                $startDateTimePicker.data("DateTimePicker").maxDate(event.date);
            });
        });
    }

    // 文件上传
    if ($.fn.fileinput != null) {
        $("[data-provide='fileinput']").each(function () {
            var $element = $(this);
            var fileType = $element.data("file-type");
            var showPreview = $element.data("show-preview");
            var showRemove = $element.data("show-remove");
            var allowedFileExtensions;

            switch (fileType) {
                case "MEDIA":
                    allowedFileExtensions = "".split(",");
                    break;
                case "FILE":
                    allowedFileExtensions = "".split(",");
                    break;
                default:
                    allowedFileExtensions = "jpg,jpeg,png,gif".split(",");
            }

            $element.addClass("is-loaded")

            var $file = $('<input name="file" type="file">').insertAfter($element).fileinput({
                uploadUrl: "/admin/upload",
                uploadExtraData: {
                    fileType: fileType != null ? fileType : "IMAGE"
                },
                allowedFileExtensions: allowedFileExtensions,
                maxFileSize: 10 * 1024,
                maxFileCount: 1,
                autoReplace: true,
                showUpload: false,
                showRemove: !!showRemove,
                showClose: false,
                showUploadedThumbs: false,
                dropZoneEnabled: false,
                initialPreview: $element.data("image-url"),
                initialPreviewAsData: true,
                initialPreviewShowDelete: false,
                showPreview: showPreview != null ? showPreview : true,
                previewClass: "single-file-preview",
                layoutTemplates: {
                    footer: '<div class="file-thumbnail-footer">{actions}</div>',
                    actions: '<div class="file-actions"><div class="file-footer-buttons">{upload} {download} {delete} {zoom} {other}</div>{drag}<div class="clearfix"></div></div>'
                },
                fileActionSettings: {
                    showUpload: false,
                    showRemove: false,
                    showDrag: false
                },
                removeFromPreviewOnError: true,
                showAjaxErrorDetails: false
            }).on("fileloaded", function (event, file, previewId, index, reader) {
                $(this).fileinput("upload");
            }).on("fileuploaded", function (event, data, previewId, index) {
                var type = data.response.type;
                var message = data.response.message;
                var responseResult = data.response.result;

                if (type === "SUCCESS") {
                    $element.val(responseResult);
                } else {
                    if ($.bootstrapGrowl != null) {
                        if (message === undefined) {
                            message = data.response;
                        }
                        $.bootstrapGrowl(message, {
                            type: "danger"
                        });
                    }
                }
            }).on("filecleared fileerror fileuploaderror", function () {
                $element.val("");
            });

            $element.data("file", $file);
        });
    }

    // 文本编辑器
    if ($.fn.summernote != null) {
        $("[data-provide='editor']").summernote();
    }

    // 滚动固定
    if ($.fn.scrollToFixed != null) {
        $("[data-spy='scrollToFixed']").each(function () {
            var $element = $(this);

            $element.scrollToFixed($element.data());
        });
    }

    // 图片延迟加载
    if ($.fn.lazyload != null) {
        var lazy = lazyload();
        lazy.loadImages();
    }

});
